#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#include <tuple>
#include <set>
#include <algorithm>
#include "log.h"
#include "toolfun.h"
#include "haskelltool.h"
using namespace  HaskellTool;

std::set<std::string> _opset = {"+", "-", "*", "/"};

int getOpPriority(std::string op)
{
    if ((op == "+") || (op == "-"))
    {
        return 1;
    }
    else if ((op == "*") || (op == "/"))
    {
        return 2;
    }
    else
    {
        return 0;
    }
}

double calStr(double x, double y, std::string op)
{
    if ((op == "+"))
    {
        return x + y;
    }
    else if ((op == "-") )
    {
        return x - y;
    }
    else if ((op == "*") )
    {
        return x * y;
    }
    else if ((op == "/") )
    {
        return x / y;
    }
    else
    {
        return x;
    }
}

/*
take5 ::(RealFloat a) => [a]->[String]-> a
take5 (x:y:xs) (op1:ops) = take5 ((calStr y x op1):xs) ops
take5 (x:xs) ops = x
*/
double task5(std::vector<double> & xs, std::vector<std::string> & ops)
{
    std::reverse(xs.begin(), xs.end());
    std::reverse(ops.begin(), ops.end());
    if ((xs.size() >= 2) && (ops.size() >= 1))
    {
        double x = xs[0];
        double y = xs[1];
        double z = calStr(x,y,ops[0]);
        ops.erase(ops.begin());
        xs.erase(xs.begin(), xs.begin() + 2);
        xs.insert(xs.begin(), z);
        return task5(xs, ops);
    }
    else
    {
        return xs[0];
    }
}


/*

take4 ::(RealFloat a, Read a) => ([a], [String])->[String]->([a], [String])
take4 (digits, operators) [] = (digits, operators)
take4 (digit1:digits, op1:operators) (x:x2:xs)
    | x `elem` ["+","-", "*", "/"] =
        if getOpPriority2 x >  getOpPriority2 op1
            then  take4 (calStr digit1 (read x2) x : digits,  op1:operators)  (xs)
            else  take4 ( digit1:digits,  x:op1:operators)  (x2:xs)
    | otherwise =  take4 ( read x : digit1:digits, op1:operators)  (x2:xs)
take4 (digits, operators) (x:xs)
    | x `elem` ["+","-", "*", "/"] = take4 (digits,  (x:operators))  xs
    | otherwise =  take4 ( read x : digits, operators)  xs

*/
typedef  std::tuple<std::vector<double>, std::vector<std::string>> Matches;
//57
Matches
take4 (Matches &matchArray, std::vector<std::string>& xs)
{
    if (xs.size() <= 0)
    {
        return matchArray;
    }
    std::vector<double> &digits = std::get<0>(matchArray);
    std::vector<std::string> &operators =  std::get<1>(matchArray);

    if ((digits.size() >= 1 ) && (operators.size() >= 1) && (xs.size() >= 2))
    {
        if (_opset.end() !=  _opset.find(xs[0]))
        {
            if (getOpPriority(xs[0]) > getOpPriority(operators[0]))
            {
                double z = calStr(digits[0], ToolFun::stringToNum<double>(xs[1]) , xs[0]);
                digits.erase(digits.begin());
                digits.insert(digits.begin(), z);
                xs.erase(xs.begin(), xs.begin() + 2);
                take4(matchArray, xs);
            }
            else
            {
                std::string str = xs[0];
                operators.insert(operators.begin(),  str);
                xs.erase(xs.begin());
                take4(matchArray , xs);
            }
        }
        else
        {
            double z = ToolFun::stringToNum<double>(xs[0]);
            digits.insert(digits.begin(), z);
            xs.erase(xs.begin());
            take4(matchArray , xs);
        }
        return  matchArray;
    }


    if (_opset.end() !=  _opset.find(xs[0]))
    {
        std::string str = xs[0];
        operators.insert(operators.begin(),  str);
        xs.erase(xs.begin());
        take4(matchArray , xs);
    }
    else
    {
        double z = ToolFun::stringToNum<double>(xs[0]);
        digits.insert(digits.begin(), z);
        xs.erase(xs.begin());
        take4(matchArray , xs);
    }

    return matchArray;
}
//39
Matches
take4a (Matches &matchArray, std::vector<std::string>& xs)
{
    if (xs.size() <= 0)
    {
        return matchArray;
    }
    std::vector<double> &digits = std::get<0>(matchArray);
    std::vector<std::string> &operators =  std::get<1>(matchArray);
    int isInOpSet = _opset.find(xs[0]) != _opset.end();

    if (  (digits.size() >= 1 ) && (operators.size() >= 1) && (xs.size() >= 2)
          && (isInOpSet) && (getOpPriority(xs[0]) > getOpPriority(operators[0])))
    {
        double z = calStr(digits[0], ToolFun::stringToNum<double>(xs[1]) , xs[0]);
        digits.erase(digits.begin());
        digits.insert(digits.begin(), z);
        xs.erase(xs.begin(), xs.begin() + 2);
        return take4a(matchArray, xs);
    }

    if (isInOpSet)
    {
        operators.insert(operators.begin(),  xs[0]);
        xs.erase(xs.begin());
        take4a(matchArray , xs);
    }
    else
    {
        double z = ToolFun::stringToNum<double>(xs[0]);
        digits.insert(digits.begin(), z);
        xs.erase(xs.begin());
        take4a(matchArray , xs);
    }
    return matchArray;
}

/*
takeAll ::(RealFloat a, Read a) => String-> a
takeAll str = take5 aa bb
    where   (aa, bb) = take4 ([], []) (words str)
*/
//std::vector<double>, std::vector<std::string>
template<typename T>
void printvector(const char *prestr, std::vector<T> vectorPrint)
{
    std::cout <<prestr<<":";
    for (T &item : vectorPrint)
    {
        std::cout << item << "  ";
    }
    std::cout <<std::endl;
}
void testfun()
{
    std::cout<<"test:" << calStr(1,3, "*") << "\n";
    std::cout<<"test:" << calStr(1,3, "/") << "\n";
    std::cout<<"test:" << calStr(1,3, "-") << "\n";
    std::cout<<"test:" << calStr(1,3, "+") << "\n";
}
double solveStr(std::string str)
{
    std::vector<double> digits;
    std::vector<std::string> operators;
    Matches matchArray(digits, operators);

    std::vector<std::string> strList= words(str);
    Matches matchArray2 = take4a(matchArray, strList );
    std::vector<double> &digits2 = std::get<0>(matchArray2);
    std::vector<std::string> &operators2 = std::get<1>(matchArray2);

    printvector<double>("digit2", digits2);
    printvector<std::string>("operators2", operators2);

    double result = task5(digits2, operators2);
    return  result;
}
